<!-- 
  为什么需要vdom?
    1）操作真实DOM比较慢，浏览器运行js快，使用js模拟真实DOM。目的可以快速响应视图更新。
    2）使用vdom实现数据驱动视图，vue:MVVM, react:setState。

  snabbdom源码：
    1）看任何框架的源码时，只看主干和要点，不要支扣细节。
    2）源码是使用ts写的，ts仅仅是类型约束，不需要关注类型约束。

  h函数：
    1）作用：生成一个vnode。
    2）源码位置：src/h.ts
    3）输入：function h(sel: string, data: VNodeData, children: VNodeChildren): VNode
       返回：return vnode(sel, data, children, text, undefined);

  vnode函数：
    1）作用：生成一个vnode（使用js去模拟真实DOM）
    2）源码位置： src/vnode.ts
    3）返回：{sel, data, children, text, elm, key};  sel表示你要生成的什么样的标签，
      <div><span>span</span></div> 对于div来产，children就是<span>span</span>
      <div>hello vdom</div> 对于div来产，children是没有的，text是hello vdom
      children, text 是不共存，要么有children，要么有text

  patch函数：
    1）作用：初始渲染把vdom转化成真实dom，真实dom挂载到容器上。 数据变了，产生新的vdom，新旧vdom进行对比，这个对比的过程就是diff的过程（diff算法）,找到了差异，只去更新差异。
      也就是说第1次调用patch是初始渲染，后面再去调用path就是新旧vdom对比差异，更新差异。
    2）源码位置：src/snabbdom.ts   当中有一个init函数，调用init函数，得到了patch函数
    3）输入：function patch(oldVnode: VNode | Element, vnode: VNode): VNode
      初次渲染，patch函数的第一个参数是一个容器，更新阶段，第一个参数是老的vnode
    4）对于patch函数来说，需要考虑两种情况，第一个参数要么是vNode，要么是Element两种情况。

  diff算法：
    假如patch函数的第一个参数是vnode,就需要进行diff算法了。新旧vnode都是树结构。
    就需要对比这两颗树了，目的是找到差异：
      1）先遍历tree1，再去遍历tree2，找到差异，不需要排序，1000个节点，要计算出1亿次，算法复杂度n^3。算法不可用。
      2）A）只比较同一层级，不进行跨级比较。
         B）如果tag不相同，直接删除重建。不会再深度比较了，目的是为了快速找到差异。
         C）如果tag和key，都相同，则认为是相同的节点，不再比较，进行复用。

  总结：
    核心概念：
      h函数   目的是创建vnode       h函数中调用vnode函数。
      patch 
        1）初次渲染  patch(容器，vnode)
        2）更新  patch(oldVnode，newVnode)
          对比时：
            如果是同一个节点：深度一层层比较 patchVnode(oldVnode, vnode, insertedVnodeQueue)
              1）老的vnode有children  新的vnode没有children，有text
                ==> 把老的vnode中的children删除掉，设置成新的vnode中的text，走removeVnodes
              2）老的vnode有children，新的vnode也有children，走updateChildren（是diff算法最麻烦的地方）
              3）老的vnode没有children，新的vnode有children，走addVnodes

            如果不是同一个节点：直接重建，不再深度一层层比较

    diff:
      1）老的vnode有children，新的vnode没有children，走removeVnodes
      2）老的vnode没有children，新的vnode有children，走addVnodes
      3）老的vnode有children，新的vnode也有children，走updateChildren

    updateChildren：
      定义了四个指定，头头，尾尾，头尾，尾头比较，细节不是关键，算法的过程也是不是核心。
      如果上面的四种情况都没有命中，此时就体现出了key的重要性。如果有了key，有复用的节点，就尽可能复用了。

    总结：
      1）响应式，监听data的数据变化的。
      2）vdom:
        A） h函数 创建一个vnode    vnode函数创建一个vnode结构
        B） patch函数  初始渲染和更新  
        C） patchVnode(diff算法)
              => 调用addVnodes
              => 调用removeVnodes
              => 调用updateChildren

 -->